#define ForEachMember(operation)                \
    for (uint8 uiRow=0; uiRow<Rows; ++uiRow)   \
    for (uint8 uiCol=0; uiCol<Cols; ++uiCol)   \
    {                                           \
        operation;                              \
    }                                           \

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>::RBMatrix()
{

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>::RBMatrix(Type scalar)
{

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>::RBMatrix(Type m00, Type m01, Type m02, 
                                     Type m10, Type m11, Type m12, 
                                     Type m20, Type m21, Type m22)
{
    RBCompileAssert(Rows == 3);
    RBCompileAssert(Cols == 3);
    (*this)(0,0) = m00;  (*this)(0,1) = m01; (*this)(0,2) = m02; 
    (*this)(1,0) = m10;  (*this)(1,1) = m11; (*this)(1,2) = m12; 
    (*this)(2,0) = m20;  (*this)(2,1) = m21; (*this)(2,2) = m22; 
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>::RBMatrix(Type m00, Type m01, Type m02, Type m03, 
                                     Type m10, Type m11, Type m12, Type m13, 
                                     Type m20, Type m21, Type m22, Type m23, 
                                     Type m30, Type m31, Type m32, Type m33)
{
    RBCompileAssert(Rows == 4);
    RBCompileAssert(Cols == 4);
    (*this)(0,0) = m00;  (*this)(0,1) = m01; (*this)(0,2) = m02; (*this)(0,3) = m03; 
    (*this)(1,0) = m10;  (*this)(1,1) = m11; (*this)(1,2) = m12; (*this)(1,3) = m13; 
    (*this)(2,0) = m20;  (*this)(2,1) = m21; (*this)(2,2) = m22; (*this)(2,3) = m23; 
    (*this)(3,0) = m30;  (*this)(3,1) = m31; (*this)(3,2) = m32; (*this)(3,3) = m33; 
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>::~RBMatrix()
{

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
void RBMatrix<Type, Rows, Cols>::GetRow(uint8 uiRow, RBVector<Type, Cols>& vec) const
{
    for (uint8 uiCol=0; uiCol<Cols; ++uiCol)
    {
        vec(uiCol) = (*this)(uiRow,uiCol);
    }
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
void RBMatrix<Type, Rows, Cols>::GetCol(uint8 uiCol, RBVector<Type, Rows>& vec) const
{
    for (uint8 uiRow=0; uiRow<Rows; ++uiRow)
    {
        vec(uiRow) = (*this)(uiRow,uiCol);
    }
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
Type& RBMatrix<Type, Rows, Cols>::operator()(uint8 uiRow, uint8 uiCol)
{
    return Data[uiRow + uiCol * Rows];
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
Type RBMatrix<Type, Rows, Cols>::operator()(uint8 uiRow, uint8 uiCol) const
{
    return Data[uiCol * Rows + uiRow];
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols> RBMatrix<Type, Rows, Cols>::operator-() const
{
    ThisType out;
    ForEachMember(out(uiRow, uiCol) = -(*this)(uiRow,uiCol));
    return out;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols> RBMatrix<Type, Rows, Cols>::operator+(const ThisType& rhs) const
{
    ThisType out;
    ForEachMember(out(uiRow, uiCol) = (*this)(uiRow,uiCol) + rhs(uiRow, uiCol));
    return out;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols> RBMatrix<Type, Rows, Cols>::operator-(const ThisType& rhs) const
{
    ThisType out;
    ForEachMember(out(uiRow, uiCol) = (*this)(uiRow,uiCol) - rhs(uiRow, uiCol));
    return out;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols> operator*(Type scalar, const RBMatrix<Type, Rows, Cols>& mat)
{
    ThisType out;
    ForEachMember(out(uiRow, uiCol) = scalar * mat(uiRow, uiCol));
    return out;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols> RBMatrix<Type, Rows, Cols>::operator*(const ThisType& rhs) const
{
    RBCompileAssert(Rows == Cols);

    ThisType out;
    for (uint8 uiRow=0; uiRow<Rows; ++uiRow)
    {
        for (uint8 uiCol=0; uiCol<Rows; ++uiCol)
        {
            out(uiRow, uiCol) = 0;
            for (uint8 ui=0; ui<Rows; ++ui)
            {
                out(uiRow, uiCol) += (*this)(uiRow,ui) * rhs(ui, uiCol);
            }
        }
    }
    return out;

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBVector<Type,Rows> RBMatrix<Type, Rows, Cols>::operator*(const RBVector<Type,Cols>& vec) const
{
    RBVector<Type,Rows> out;

    for (uint8 uiRow=0; uiRow<Rows; ++uiRow)
    {
        out(uiRow) = 0;
        for (uint8 uiCol=0; uiCol<Cols; ++uiCol)
        {
            out(uiRow) += (*this)(uiRow,uiCol) * vec(uiCol);
        }
    }

    return out;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>& RBMatrix<Type, Rows, Cols>::operator=(const RBMatrix<Type, Rows, Cols>& rhs)
{
    ForEachMember((*this)(uiRow,uiCol) = rhs(uiRow,uiCol));
    return *this;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>& RBMatrix<Type, Rows, Cols>::operator+=(const ThisType& rhs)
{
    ForEachMember((*this)(uiRow,uiCol) += rhs(uiRow,uiCol));
    return *this;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>& RBMatrix<Type, Rows, Cols>::operator-=(const ThisType& rhs)
{
    ForEachMember((*this)(uiRow,uiCol) -= rhs(uiRow,uiCol));
    return *this;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>& RBMatrix<Type, Rows, Cols>::operator*=(const ThisType& rhs)
{
    *this = rhs * (*this);
    return *this;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols>& RBMatrix<Type, Rows, Cols>::operator*=(Type scalar)
{
    ForEachMember((*this)(uiRow,uiCol) *= scalar);
    return *this;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
void RBMatrix<Type, Rows, Cols>::SetIdentity()
{
    RBCompileAssert(Rows == Cols);
    ForEachMember((*this)(uiRow,uiCol) = (uiRow == uiCol) ? (Type)1 : (Type)0);
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
RBMatrix<Type, Rows, Cols> RBMatrix<Type, Rows, Cols>::Transpose() const
{
    RBCompileAssert(Rows == Cols);
    ThisType out;
    ForEachMember(out(uiRow,uiCol) = (*this)(uiCol,uiRow));
    return out;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
void RBMatrix<Type, Rows, Cols>::SetXRotation(Type xRot)
{
    RBCompileAssert(Rows == 3);
    RBCompileAssert(Cols == 3);
    Type sn, cs;
    RBMath::SinCos(xRot, sn, cs);

    (*this)(0,0) = 1.0f;
    (*this)(0,1) = 0.0f;
    (*this)(0,2) = 0.0f;
    (*this)(1,0) = 0.0f;
    (*this)(1,1) = cs;
    (*this)(1,2) = sn;
    (*this)(2,0) = 0.0f;
    (*this)(2,1) = -sn;
    (*this)(2,2) = cs;

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
void RBMatrix<Type, Rows, Cols>::SetYRotation(Type yRot)
{
    RBCompileAssert(Rows == 3);
    RBCompileAssert(Cols == 3);
    Type sn, cs;
    RBMath::SinCos(yRot, sn, cs);

    (*this)(0,0) = cs;
    (*this)(0,1) = 0.0f;
    (*this)(0,2) = -sn;
    (*this)(1,0) = 0.0f;
    (*this)(1,1) = 1.0f;
    (*this)(1,2) = 0.0f;
    (*this)(2,0) = sn;
    (*this)(2,1) = 0.0f;
    (*this)(2,2) = cs;

}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
void RBMatrix<Type, Rows, Cols>::SetZRotation(Type zRot)
{
    RBCompileAssert(Rows == 3);
    RBCompileAssert(Cols == 3);
    float sn, cs;
    RBMath::SinCos(zRot, sn, cs);

    (*this)(0,0) = cs;
    (*this)(0,1) = sn;
    (*this)(0,2) = 0.0f;
    (*this)(1,0) = -sn;
    (*this)(1,1) = cs;
    (*this)(1,2) = 0.0f;
    (*this)(2,0) = 0.0f;
    (*this)(2,1) = 0.0f;
    (*this)(2,2) = 1.0f;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
const RBMatrix<Type, Rows, Cols>& RBMatrix<Type, Rows, Cols>::Identity()
{
    static ThisType ms_Identity;
    static bool bFirst = true;
    if (bFirst)
    {
        ms_Identity.SetIdentity();
        bFirst = false;
    }
    return ms_Identity;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
Type* RBMatrix<Type, Rows, Cols>::GetCol( uint8 uiCol )
{
    return &Data[uiCol * Rows];
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
template<typename Type, uint32 Rows, uint32 Cols>
const Type* RBMatrix<Type, Rows, Cols>::GetCol( uint8 uiCol ) const
{
    return &Data[uiCol * Rows];
}
